﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Security.Cryptography;
using System.IO;

namespace Discio
{
    /// <summary>
    /// AES Encrytion class (Rijndael)
    /// </summary>
    public class AES : IDisposable
    {
        // Change these keys
        private byte[] Key = { 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 };
        private byte[] Vector = { 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 221, 112, 79, 32, 114, 156 };

        private ICryptoTransform encryptorTransform;
        private ICryptoTransform decryptorTransform;
        private UTF8Encoding utfEncoder;
        private RijndaelManaged rij;
         
        public AES()
        { 
            rij = new RijndaelManaged();
             
            encryptorTransform = rij.CreateEncryptor(this.Key, this.Vector);
            decryptorTransform = rij.CreateDecryptor(this.Key, this.Vector);
 
            utfEncoder = new UTF8Encoding();
        }


        /// <summary>
        /// Generates an encryption key
        /// </summary>
        /// <returns>byte[]</returns>
        static public byte[] GenerateEncryptionKey()
        {
            //Generate a Key.
            RijndaelManaged rij = new RijndaelManaged();
            rij.GenerateKey();
            return rij.Key;
        }

        /// <summary>
        /// Generates a unique encryption vector
        /// </summary>
        /// <returns>byte[]</returns>
        static public byte[] GenerateEncryptionVector()
        {
            //Generate a Vector
            RijndaelManaged rij = new RijndaelManaged();
            rij.GenerateIV();
            return rij.IV;
        }

        /// <summary>
        /// Encrypt  text to encrypted string
        /// </summary>
        /// <param name="TextValue">Text to encrypt</param>
        /// <returns>string</returns>
        public string EncryptToString(string TextValue)
        {
            return ByteArrayToString(Encrypt(TextValue));
        }

        /// <summary>
        /// Decrypt string
        /// </summary>
        /// <param name="encryptedString">encryptedString</param>
        /// <returns>Decrypted string</returns>
        public string DecryptString(string encryptedString)
        {
            return Decrypt(StringToByteArray(encryptedString));
        }


        /// <summary>
        /// Encrypt text to encrypted byte array
        /// </summary>
        /// <param name="TextValue"></param>
        /// <returns></returns>
        public byte[] Encrypt(string TextValue)
        { 
            Byte[] bytes = utfEncoder.GetBytes(TextValue);
             
            MemoryStream memoryStream = new MemoryStream();
             
             
            CryptoStream cs = new CryptoStream(memoryStream, encryptorTransform, CryptoStreamMode.Write);
            cs.Write(bytes, 0, bytes.Length);
            cs.FlushFinalBlock();
             
            memoryStream.Position = 0;
            byte[] encrypted = new byte[memoryStream.Length];
            memoryStream.Read(encrypted, 0, encrypted.Length);

             
            cs.Close(); 

            return encrypted;
        }



        /// <summary>
        /// Decrypt byte[]
        /// </summary>
        /// <param name="encryptedValue">encryptedValue</param>
        /// <returns>Decrypted string</returns>
        public string Decrypt(byte[] encryptedValue)
        {
            #region Write the encrypted value to the decryption stream
            MemoryStream encryptedStream = new MemoryStream();
            CryptoStream decryptStream = new CryptoStream(encryptedStream, decryptorTransform, CryptoStreamMode.Write);
            decryptStream.Write(encryptedValue, 0, encryptedValue.Length);
            decryptStream.FlushFinalBlock();
            #endregion

            #region Read the decrypted value from the stream.
            encryptedStream.Position = 0;
            Byte[] decryptedBytes = new Byte[encryptedStream.Length];
            encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
            encryptedStream.Close();
            #endregion
            return utfEncoder.GetString(decryptedBytes);
        }

        /// <summary>
        /// Convert a string to a byte array
        /// </summary>
        /// <remarks>
        /// Ensure that it can be used as part of a URL (Do not use ASCIIEncoding)
        /// </remarks>
        /// <param name="stringValue">stringValue</param>
        /// <returns>byte[]</returns>
        public byte[] StringToByteArray(string stringValue)
        {
            if (stringValue.Length == 0) { 
                throw new Exception("Invalid string value in StrToByteArray");
            }
                 
            byte val;
            byte[] byteArr = new byte[stringValue.Length / 3];
            int i = 0;
            int j = 0;
            do
            {
                val = byte.Parse(stringValue.Substring(i, 3));
                byteArr[j++] = val;
                i += 3;
            }
            while (i < stringValue.Length);
            return byteArr;
        }

        /// <summary>
        /// Convert a byte array to string
        /// </summary>
        /// <remarks>
        /// Ensure that it can be used as part of a URL (Do not use ASCIIEncoding)
        /// </remarks>
        /// <param name="stringValue">byteArray</param>
        /// <returns>string</returns> 
        public string ByteArrayToString(byte[] byteArray){

            byte val;
            string tempString = "";
            for (int i = 0; i <= byteArray.GetUpperBound(0); i++)
            {
                val = byteArray[i];
                if (val < (byte)10){
                    tempString += "00" + val.ToString();
                }else if (val < (byte)100){
                    tempString += "0" + val.ToString();
                }
                else { 
                    tempString += val.ToString();
                }
            }
            return tempString;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (rij != null)
                {
                    rij.Dispose();
                    rij = null;
                }

                if (encryptorTransform != null)
                {
                    encryptorTransform.Dispose();
                    encryptorTransform = null; 
                }

                if (decryptorTransform != null)
                {
                    decryptorTransform.Dispose();
                    decryptorTransform = null;
                }
            }
        }

    }
}
